
const PatchFlag = {
    TEXT: 1, // 动态文本节点
    CLASS: 2, // 动态class 绑定
    STYLE: 3,
    // 其他。。
}

// Block 块
// Block 本质上也是一个虚拟DOM 节点  比普通的虚拟节点多一个 dynamicChildren 属性
// dynamicChildren 是用于记录动态子节点的数组
// Block 会收集所有的动态子节点,到根节点的dynamicChildren中
/**
 *  在渲染block 时候 会忽略掉children数组, 只更新动态内容. 
 *  动态节点中存在对应的补丁标志, 根据补丁标志进行靶向更新.
 *  
 *  !!!
 *  dynamicChildren 是收集的动态节点 是忽略虚拟DOM的层级的！！！。
 * 因此 需要让v-if v-for 等 结构化指令节点也作为block角色进行收集
 * 如 v-if 和v-else
 * v-if中就一个动态节点， 而v-else 动态节点在多层嵌套中
 * 渲染器会根据v-if 的值去更新前后两个block 并且用新的block去替换旧的block
 * 
 * v-for 则是用一个fragment 片段去包裹 渲染的内容，作为一个block
 * 如果v-for 的内容是动态的 即 v-for(i in list) list 为动态内容，
 * 这个时候fragment是结构不稳定的， 因为dynamicChildren 前后收集到的动态节点的数量或顺序不一致
 * 
 * 如果是动态的内容， 则使用fragment中children 进行diff 操作更新。
 * Fragment 的子节点 仍然是可以有block组件的数组
 * 
 * 当 list 为常量或是静态内容是，fragment 才是稳定的。
 * 
 * ！！！！！
 * 拥有多个根节点的模板的fragment 是稳定的！
 * 
 * 
 */

// 动态节点栈
const dynamicChildrenStack = []
// 当前动态节点 集合
let currentDynamicChildren = null

// openBlock 用来创建一个新的动态节点集合  并将其压入栈中
function openBlock () {
    currentDynamicChildren = []
    dynamicChildrenStack.push(currentDynamicChildren)
}

// 将openBlock 创建的动态节点结合从栈中弹出
function closeBlock () {
    currentDynamicChildren = dynamicChildrenStack.pop()
}


// 创建虚拟DOM节点函数  基本实现如下
// 参数4 表示 补丁类型
function createVNode (tag, props, children, flag) {
    const key = props && props.key
    props && delete props.key

    const vNode = {
        tag,
        props,
        children,
        key,
        patchFlags: flag,
    }

    // 动态节点
    if (typeof flag !== 'undefined' && currentDynamicChildren) {
        currentDynamicChildren.push(vNode)
    }

    return vNode
}

function createBlock (tag, props, children) {
    const block = createVNode(tag, props, children)
    // 将当前的动态节点集合作为 block.dynamicChildren
    block.dynamicChildren = currentDynamicChildren

    closeBlock()
    return block
}

// 对之前的patchElement 进行优化 
function patchElement (n1, n2) {
    const el = n2.el = n1.el
    const oldProps = n1.props || {}
    const newProps = n2.props || {}

    // 省略部分代码

    // 有缺点, 只会更新 动态节点,跳过了所有静态节点.
    // // 如果有动态节点
    // if (n2.dynamicChildren) {
    //     patchBlockChildren(n1, n2)
    // } else {
    //     patchChildren(n1.n2, el)
    // }

    if (n2.patchFlags) {
        // 靶向更新
        if (n2.patchFlags === PatchFlag.TEXT) {
            // 更新文本
        } else if (n2.patchFlags === PatchFlag.CLASS) {
            // 更新class
        } else {
            // 以此类推
        }
    } else {
        //如果没有 补丁标志则全量更新
        for (const key in newProps) {
            if (newProps[key] !== oldProps[key]) {
                patchProps(el, key, oldProps[key], newProps[key]);
            }
        }

        for (const key in oldProps) {
            if (!(key in newProps)) {
                patchProps(el, key, oldProps[key], null);
            }
        }
    }

    // 处理children 
    patchChildren(n1, n2, el)
}

function patchBlockChildren (n1, n2) {
    // 只更新动态节点
    for (let i = 0; i < n2.dynamicChildren.length; i++) {
        patchElement(n1.dynamicChildren[i], n2.dynamicChildren[i])
    }
}



function render () {
    return (openBlock(), createBlock('div', null, [
        createVNode('p', { class: 'foo' }, null, PatchFlag.TEXT),
        createVNode('p', { class: 'bar' }, null),
    ]))
}



/**
 * 静态提升！！！
 * 
 * 将静态节点 以及静态props属性 提升到 渲染函数以外
 * 
 * 当渲染 的时候 就使用这些静态节点的引用进行渲染
 *  !!!
 * 静态提升是以树为单位的！！！ 除了根元素作为block角色不会被提升以外，其他都会被提升
 *  div
 *      p
 *          span
 *      p 
 *  div
 * div内的都会被提升，div不会
 * !!!
 * 
 * 
 * */


/**
 *  预字符串化
 *  ：是在静态提升基础上提升优化策略
 *  
 * 比如多个p标签都是一样的，那么会把这些p标签节点进行序列化为字符串 并且生产一个静态的虚拟节点。
 * 
 * 
 * 优势：
 * 1. 大块的静态内容可以通过innerHTML进行设置，性能上有一定优势
 * 2. 减少创建虚拟节点产生的性能开销
 * 3. 减少内存占用
 * 
 *
 * 
 * */


/**
 * 缓存内联事件处理函数
 * 
 * 把内联事件处理函数 缓存到 组件实例的cache上， 
 * 当渲染函数重新执行并创建新的虚拟DOM树时， 优先读取缓存中的事件处理函数，
 * 如果没有才回去创建一个新事件处理函数。  避免额外的性能开销。
 * 
 * 
 * */

/**
 *  v-once
 * ：仅渲染元素和组件一次，并且跳过后须的更新。
 * 原理就是通过cache去缓存渲染函数的全部或部分的执行结果
 *  
 * 
 * 
 * 优势
 *  避免组件更新时重新创建虚拟DOM带来的性能开销。
 * 避免无用的DIFF开销。
 * */
